<?php

namespace Upfor\UpforPHP\Cache;

use InvalidArgumentException;
use Predis\Client as RedisClient;
use BadFunctionCallException;
use Upfor\UpforPHP\Cache\AbstractCache;
use Upfor\UpforPHP\Interfaces\CacheInterface;

/**
 * Predis
 */
class Predis extends AbstractCache implements CacheInterface {

    const PERSISTENT = true;

    protected $options = [
        'ttl' => 0,
        'prefix' => '',
        'servers' => [
            [
                'host' => '127.0.0.1',
                'port' => 6379,
                'weight' => 100,
                'password' => null,
            ],
        ],
        'options' => [],
    ];

    function __construct($options = array()) {
        if (empty($options['servers']) || !is_array($options['servers'])) {
            throw new InvalidArgumentException('Invalid argument for Predis');
        }

        if (!empty($options)) {
            $this->options = array_merge($this->options, $options);
        }

        !empty($this->options['prefix']) && $this->prefix = $this->options['prefix'];

        self::$cacheObj = new RedisClient($options['servers'], $this->options['options']);
    }

    /**
     * Store data in the cache
     *
     * @param  string  $key
     * @param  mixed   $value
     * @param  integer $ttl
     * @return boolean
     */
    public function set($key, $value, $ttl = null) {
        if (is_null($ttl) || $ttl < 0) {
            $ttl = (int) abs($this->options['ttl']);
        }

        $data = json_encode($value);
        $pKey = $this->createKey($key);
        if ($ttl > 0) {
            return self::$cacheObj->setex($pKey, $ttl, $data);
        } else {
            return self::$cacheObj->set($pKey, $data);
        }
    }

    /**
     * Returns TRUE if the cache key exists and FALSE if not
     *
     * @param  string $key Cache key
     * @return boolean
     */
    public function has($key) {
        return (self::$cacheObj->get($this->createKey($key)) !== false);
    }

    /**
     * Fetch data from the cache
     *
     * @param  string $key
     * @return mixed|false
     */
    public function get($key) {
        $data = self::$cacheObj->get($this->createKey($key));

        return json_decode($data, true);
    }

    /**
     * Delete data from the cache
     *
     * @param  string $key
     * @return boolean
     */
    public function delete($key) {
        return self::$cacheObj->del($this->createKey($key));
    }

    /**
     * Clears the cache
     *
     * @return boolean
     */
    public function clear() {
        return self::$cacheObj->flushDB();
    }

    /**
     * Increment
     *
     * @param  string $key
     * @param  mixed  $value
     * @return mixed
     */
    public function increment($key, $value = 1) {
        return self::$cacheObj->incrby($key, (int) $value);
    }

    /**
     * Decrement
     *
     * @param  string $key
     * @param  mixed  $value
     * @return mixed
     */
    public function decrement($key, $value = 1) {
        return self::$cacheObj->decrby($key, (int) $value);
    }

    /**
     * Is support persistent?
     *
     * @return boolean
     */
    public function persistent() {
        return self::PERSISTENT;
    }

    public function __call($method, $arguments) {
        if (!method_exists(self::$cacheObj, $method)) {
            throw new BadFunctionCallException("Redis::{$method}() is not supported");
        }

        return call_user_func_array([self::$cacheObj, $method], $arguments);
    }

}
